2019 年的時候,我因為打工的關係,下班時常常因為懶惰,就直接外帶麥當勞回家。
當時我甚至會根據麥當勞報報的抽獎結果來決定晚餐要不要吃麥當勞。
結果事情越來越往奇怪的地方發展,我又多申請了兩隻帳號,每天手動幫三隻帳號抽獎,看看能不能拿到喜歡的優惠。
最後我又覺得好麻煩,我為什麼不寫程式自動抽獎呢 ? 我是從那時候開始,踏入網路爬蟲這個無盡深淵。
擷取自第一天的文章
那時候我還不怎麼會寫程式,當時的環境是在手機裝 Package Capture 看封包。
當時我甚至不知道有 Fiddler 或 Charles 之類的工具,可以讓我看封包方便些。
麥當勞報報版本 2 的設計是,登入後會頒發一組 token 到客戶端,之後可以用這組 token 完成任何操作。
神奇的是,token 不會因為你登出和重新登入後被銷毀。代表你可以擁有多組 token,且生命週期非常長,幾乎完全不會過期。
登入的功能並不好做,因為表單中有一個欄位看起來像是亂碼。
原來該欄位會使用裝置中的一些資訊當作字串,字串相加後還會再被 MD5 雜湊過。
當時能做出來,也要感謝另一位透過這個專案認識的朋友,他負責幫我處理對 APK 的逆向工程。
這是當年對 登入 的實做。不過看看就好,這只是 legacy code。
自動抽獎的程式並不難做,只要有辦法每天去打一樣的 API 即可。
在我成功做出來之後,我又萌生一個想法,如果我把朋友圈的帳號集結起來一起抽獎,每天統計優惠,寄發到一個 Mail 信箱呢 ?
假設最後能有 20 組帳號,每天抽獎就能累積不少優惠,大家可以根據想要的優惠,自行登入他人的帳號取走優惠即可。
麥當勞報報版本 3 的設計在資安上有明顯的變強,它把封包全部加密過了。
所謂的封包加密,不是指 HTTPS 的加密,而是回應中原本該是明碼的部份也被加密過了。
當時也是強者我朋友,對 APK 進行逆向工程,發現了加密方法是 AES,並且找出 AES key 才能順利完成版本 3 的 API 爬取。
麥當勞報報後續的更新,已經把 AES key 換掉,或者根本把加密演算法改掉了。由於我們沒有繼續對程式逆向,所以不清楚。
後面也新增了 root 偵測、SSL Pinning、Proxy 偵測,以及會針對某些 IP 做阻擋的功能。
麥當勞報報如果偵測到裝置有 root,APP 將無法使用。
但如果使用 jadx 反編譯 APK。
$ jadx 麥當勞_v3.0.3_apkpure.com.apk
./麥當勞_v3.0.3_apkpure.com/sources/g/g/b/q/r0/a.java
該檔案的以下函式會回傳一個布林值,判斷裝置有無 root,其實可以讓該函式永遠回傳 false。
之後重新打包 APK 就可以繞過了。
37 /* JADX WARN: Multi-variable type inference failed */
38 /* JADX WARN: Type inference failed for: r0v5, types: [boolean, int] */
39 public final boolean a(@NotNull Context context) {
40 f0.p(context, "context");
41 int f2 = m0.b.f(l0.a, -1);
42 int i2 = f2;
43 if (1 != f2) {
44 ?? s2 = new c(context).s();
45 m0.b.p(l0.a, s2);
46 i2 = s2;
47 }
48 return i2 == 1;
49 }
SSL Pinning 剛好在昨天的文章提過,當時我是用 apk-mitm 來拆除的。
麥當勞報報如果偵測到裝置有設定 Proxy,APP 將無法使用。
我沒有特別去攻擊這個部份,但我個人猜想,用 Transparent Proxy 就解決問題了。
畢竟 Transparent Proxy 就不是在客戶端主動設定代理伺服器,而是上游節點強制讓你通過。
這個部份無解,除非有多組 IP 可以使用。
我也沒特地去測試過麥當勞報報的 rate limit 在哪裡,只知道自己對麥當勞報報主機的請求量很大,有一天發現自己被 ban 了。
以上純屬技術性討論。
總覺得還蠻神奇的,最一開始只是想解決抽獎很麻煩這個問題,所以寫程式自動化。
不過沒想到有一天,自己能對一個 APP 的資安策略有那麼大的影響。